﻿using QntPlatform.Db;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Threading.Tasks;
using System.Timers;

namespace QntPlatform.BinanceApi.DApi
{
    public class USDExchangeImp : IExchange
    {
        DApiClient client;
        public string Name => "Binance";
        /// <summary>
        /// 币种资产，BTC
        /// </summary>
        public readonly string Asset = "BTC";
        //标的交易对,BTCUSD
        public readonly string Pair = "BTCUSD";
        //symbol,交易对
        /**
         symbol": "BTCUSD_200626",  // 交易对
        "ps": "BTCUSD",      //pair
         */
        public string Currency { get; }
        public static void SymbolExp(string symbol, out string pair, out string asset)
        {
            var index = symbol.IndexOf("_");
            var pairStr = symbol;
            if (index > 0)
            {
                pairStr = symbol.Substring(0, index);
            }
            pair= pairStr;
            asset = pairStr.Replace("USD", "");
        }
        TickerListener tickerListener;
        bool IsTest = BinanceExchangeImp.isTest;
        ILog log;

        public USDExchangeImp(string apiKey, string secretKey, string currency, ILog _log = null)
        {
            if (_log != null)
            {
                log = _log;
            }
            else
            {
                log = LogManager.GetLogger();
            }
            client = new DApiClient(apiKey, secretKey, log);

            Currency = currency;
            SymbolExp(Currency,out Pair,out Asset);
            tickerListener = TickerListener.GetOrCreateListener(this);
            log.Info($"交易平台接口初始化完成：{this.GetHashCode()},apikey:{apiKey},环境：", IsTest);
        }
        public event EventHandler<Ticker> TickerChangeEvent
        {
            add
            {
                tickerListener.TickerChangeEvent += value;
            }
            remove
            {
                tickerListener.TickerChangeEvent -= value;

            }
        }


        public Task<bool> CancelOrderAsync(IConvertible id)
        {
            throw new NotImplementedException();
        }

        public Task<string> ConnTestAsync()
        {
            throw new NotImplementedException();
        }

        public async Task<IConvertible> CreateOrder(decimal amount, SideDirection sideDirection, decimal price = -1, LogInfo info = null)
        {
            var req = new CreateOrderDto();
            try
            {
                req.Quantity = getAmount(amount);
                if (price == -1)
                {
                    req.Type = CreateOrderDto.OrderType.Market;
                }
                else
                {
                    req.Type = CreateOrderDto.OrderType.Limit;//
                    req.Price = price;
                    req.TimeInForce = CreateOrderDto.TimeInForceEnum.GTC;
                }
                req.Symbol = Currency;
                req.Side = (sideDirection is SideDirection.CloseSell or SideDirection.Buy) ? CreateOrderDto.OrderSide.Buy : CreateOrderDto.OrderSide.Sell;
                req.PositionSide = (sideDirection is SideDirection.CloseBuy or SideDirection.Buy) ? CreateOrderDto.PositionSideType.LONG : CreateOrderDto.PositionSideType.SHORT;
                req.NewOrderRespType = CreateOrderDto.NewOrderResponseType.Acknowledge;
                req.NewClientOrderId = info?.ActId;
                if (IsTest)
                {
                    var tRe = client.CreateOrderTest(req);
                    var testId = $"{Name}_{Currency}_test_{req.GetHashCode()}";
                    if (string.IsNullOrWhiteSpace(req.NewClientOrderId))
                        req.NewClientOrderId = testId;
                    log.InfoByOrder("订单创建完成", this, sideDirection, amount, price, req.NewClientOrderId, null, testId, info);
                    return testId;
                }
                var resp = await client.CreateOrder(req);
                if (string.IsNullOrWhiteSpace(req.NewClientOrderId))
                    req.NewClientOrderId = resp.OrderId + "";
                log.InfoByOrder("订单创建完成", this, sideDirection, amount, price, req.NewClientOrderId, null, resp.OrderId + "", info);
                return resp.OrderId;
            }
            catch (System.Exception ex)
            {
                //log.Error("交易异常:" + ex, new { req }, msg3: LogType);
                log.ErrorByOrder(ex, this, sideDirection, amount, price, info?.ActId, null, info);
                return null;
            }

        }

        public async Task<Account> GetAccountAsync()
        {
            var re = await client.GetBalance().CheckResp();
            var info = re.First(p => p.asset == Asset);//Currency
            var acc = new Account();
            acc.Balance = info.availableBalance;
            acc.Info = info;
            return acc;
        }
        ///price,amount
        public int[] TradePrecision { get; private set; } = new[] { 3, 3 };
        public decimal getAmount(decimal amount)
        {
            var re = Math.Round(amount, TradePrecision[1], MidpointRounding.ToNegativeInfinity);
            return re;
        }
        public decimal FmtPrice(decimal price)
        {
            var re = Math.Round(price, TradePrecision[0], MidpointRounding.ToNegativeInfinity);
            return re;
        }

        public Order GetOrder(IConvertible id)
        {
            throw new NotImplementedException();
        }

        public Position[] GetPosition(string contractType)
        {
            var pr = client.GetPositionRisk(pair: contractType);
            var pstInfos = pr.Where(p => p.symbol == contractType).ToArray();
            var psts = new List<Position>();
            foreach (var pstInfo in pstInfos)
            {
                var pst = new Position();
                pst.Amount = pstInfo.positionAmt;
                pst.ContractType = pstInfo.symbol;
                //pst.FrozenAmount=pstInfo.positionAmt;
                pst.MarginLevel = pstInfo.leverage;
                pst.Price = pstInfo.entryPrice;
                pst.Profit = pstInfo.unRealizedProfit;
                switch (pstInfo.positionSide)
                {
                    case CreateOrderDto.PositionSideType.LONG:
                        pst.Type = SideDirection.Buy;
                        break;
                    case CreateOrderDto.PositionSideType.SHORT:
                        pst.Type = SideDirection.Sell;
                        break;
                    case CreateOrderDto.PositionSideType.BOTH:
                        pst.Type = SideDirection.Buy;
                        break;
                }
                psts.Add(pst);
            }
            return psts.ToArray();
        }

        public async Task<Record[]> GetRecordsAsync(int period, int limit = 30)
        {
            var list = await client.GetKlines(Currency, KlineCandleStick.PeriodConvert(period), limit: limit);
            var re = list.Select(p => new Record() { Close = p.Close, Volume = p.Volume, High = p.High, Low = p.Low, Open = p.Open, Time = p.CloseTime.Ticks }).ToArray();
            return re;
        }
        //BTCUSD_PERP,ETHUSD_PERP,LINKUSD_PERP,BNBUSD_PERP,TRXUSD_PERP,DOTUSD_PERP,ADAUSD_PERP,EOSUSD_PERP,LTCUSD_PERP,BCHUSD_PERP,XRPUSD_PERP,ETCUSD_PERP,BTCUSD_210326,ETHUSD_210326,ADAUSD_210326,LINKUSD_210326,FILUSD_PERP,BNBUSD_210326,DOTUSD_210326,XRPUSD_210326,LTCUSD_210326,BCHUSD_210326,FILUSD_210326,EGLDUSD_PERP,BTCUSD_210625,ETHUSD_210625,ADAUSD_210625,LINKUSD_210625,BCHUSD_210625,DOTUSD_210625,XRPUSD_210625,LTCUSD_210625,BNBUSD_210625,DOGEUSD_PERP
        public async Task<Ticker> GetTickerAsync()
        {
            var resp = await client.GetTickerPrice(pair: Pair);
            var tp = resp.SingleOrDefault(p => p.ps == Pair && p.Symbol == Currency);
            if (tp == null)
                return null;
            var tck = new Ticker();
            tck.Buy = tp.Price;
            tck.Sell = tp.Price;
            tck.Last = tp.Price;
            return tck;
        }
    }
}
